import {
  ComputeBudgetProgram,
  Connection,
  Keypair,
  PublicKey,
  TransactionMessage,
  VersionedTransaction,
} from '@solana/web3.js';
import {
  createAssociatedTokenAccountIdempotentInstruction,
  createCloseAccountInstruction,
  getAccount,
  getAssociatedTokenAddress,
  getAssociatedTokenAddressSync,
  RawAccount,
  TOKEN_PROGRAM_ID,
  AccountLayout,
} from '@solana/spl-token';
import { Liquidity, LiquidityPoolKeysV4, LiquidityStateV4, Percent, Token, TokenAmount } from '@raydium-io/raydium-sdk';
import { MarketCache, PoolCache, SnipeListCache } from './cache';
import { PoolFilters, PoolSizeFilter } from './filters';
import { TransactionExecutor } from './transactions';
import { createPoolKeys, logger, NETWORK, sleep } from './helpers';
import { Mutex } from 'async-mutex';
import BN from 'bn.js';
import { WarpTransactionExecutor } from './transactions/warp-transaction-executor';
import { JitoTransactionExecutor } from './transactions/jito-rpc-transaction-executor';

export interface BotConfig {
  wallet: Keypair;
  checkRenounced: boolean;
  checkFreezable: boolean;
  checkBurned: boolean;
  minPoolSize: TokenAmount;
  maxPoolSize: TokenAmount;
  quoteToken: Token;
  quoteAmount: TokenAmount;
  quoteAta: PublicKey;
  oneTokenAtATime: boolean;
  useSnipeList: boolean;
  autoSell: boolean;
  autoBuyDelay: number;
  autoSellDelay: number;
  maxBuyRetries: number;
  maxSellRetries: number;
  unitLimit: number;
  unitPrice: number;
  takeProfit: number;
  stopLoss: number;
  buySlippage: number;
  sellSlippage: number;
  priceCheckInterval: number;
  priceCheckDuration: number;
  filterCheckInterval: number;
  filterCheckDuration: number;
  consecutiveMatchCount: number;
}

export class Bot {
  private readonly poolFilters: PoolFilters;

  private readonly poolSizeFilter: PoolSizeFilter;

  // snipe list
  private readonly snipeListCache?: SnipeListCache;

  // one token at the time
  private readonly mutex: Mutex;
  private sellExecutionCount = 0;
  public readonly isWarp: boolean = false;
  public readonly isJito: boolean = false;

  // 添加轮询相关的属性
  private readonly pollIntervals: Map<string, NodeJS.Timeout> = new Map();
  private readonly pollTimeouts: Map<string, NodeJS.Timeout> = new Map();
  private readonly POLL_INTERVAL = 2000; // 轮询间隔2秒
  private readonly MAX_POLL_DURATION = 180000; // 最大轮询时间3分钟

  // 添加钱包保护相关变量
  private initialWalletBalance: number = 0;
  private todayAllowBuy: boolean = true;
  private walletBalanceInterval: NodeJS.Timeout | null = null;
  private readonly enableWalletProtection: boolean = process.env.ENABLE_WALLET_PROTECTION === 'true';
  private readonly minWalletBalance: number = Number(process.env.MIN_WALLET_BALANCE || '0.045');
  private readonly maxWalletBalance: number = Number(process.env.MAX_WALLET_BALANCE || '0.065');
  private floatingStopLossPoint: number = 0; // 添加浮动止损点属性

  // 添加全局卖出状态跟踪
  private readonly soldTokens: Set<string> = new Set();

  constructor(
    private readonly connection: Connection,
    private readonly connection0: Connection,
    private readonly connection1: Connection,
    private readonly connection2: Connection,
    private readonly marketStorage: MarketCache,
    private readonly poolStorage: PoolCache,
    private readonly txExecutor: TransactionExecutor,
    readonly config: BotConfig,
  ) {
    this.isWarp = txExecutor instanceof WarpTransactionExecutor;
    this.isJito = txExecutor instanceof JitoTransactionExecutor;

    this.mutex = new Mutex();

    // 初始化钱包余额检查
    if (this.enableWalletProtection) {
      this.startWalletBalanceCheck();
    }

    // 初始化浮动止损点
    this.initFloatingStopLossPoint();

    this.poolSizeFilter = new PoolSizeFilter(
      this.connection0,
      this.config.quoteToken,
      this.config.minPoolSize,
      this.config.maxPoolSize,
    );

    this.poolFilters = new PoolFilters(connection2, {
      quoteToken: this.config.quoteToken,
      minPoolSize: this.config.minPoolSize,
      maxPoolSize: this.config.maxPoolSize,
    });

    if (this.config.useSnipeList) {
      this.snipeListCache = new SnipeListCache();
      this.snipeListCache.init();
    }
  }

  private async initFloatingStopLossPoint() {
    try {
      const tokenBalance = await this.connection2.getTokenAccountBalance(this.config.quoteAta);
      if (tokenBalance && tokenBalance.value) {
        const currentBalance = tokenBalance.value.uiAmount || 0;
        this.floatingStopLossPoint = currentBalance * 0.8;
        logger.info(
          {
            currentBalance,
            initialFloatingStopLoss: this.floatingStopLossPoint,
          },
          '初始化浮动止损点',
        );
      }
    } catch (error) {
      logger.error({ error: error instanceof Error ? error.message : String(error) }, '初始化浮动止损点失败');
    }
  }

  private startWalletBalanceCheck() {
    // 立即执行一次检查
    this.checkWalletBalance().then((result) => {
      this.todayAllowBuy = result;
    });

    // 设置定时检查
    this.walletBalanceInterval = setInterval(async () => {
      this.todayAllowBuy = await this.checkWalletBalance();
      // logger.info({ todayAllowBuy: this.todayAllowBuy }, '当前钱包余额状态');
    }, 60000); // 每30秒检查一次
  }

  private async checkWalletBalance(): Promise<boolean> {
    try {
      const tokenBalance = await this.connection2.getTokenAccountBalance(this.config.quoteAta);

      if (!tokenBalance || !tokenBalance.value) {
        logger.warn('无法获取代币余额');
        return true;
      }

      const balanceInToken = tokenBalance.value.uiAmount || 0;

      // 检查是否触发固定止损点或浮动止损点
      const isFixedStopLossTriggered = this.minWalletBalance > 0 && balanceInToken <= this.minWalletBalance;
      const isFloatingStopLossTriggered =
        this.floatingStopLossPoint > 0 && balanceInToken <= this.floatingStopLossPoint;
      const isMaxBalanceReached = balanceInToken >= this.maxWalletBalance;

      if (isFixedStopLossTriggered || isFloatingStopLossTriggered || isMaxBalanceReached) {
        logger.info(
          {
            currentBalance: balanceInToken,
            fixedStopLoss: this.minWalletBalance,
            floatingStopLoss: this.floatingStopLossPoint,
            maxAllowed: this.maxWalletBalance,
            triggerType: isFixedStopLossTriggered ? '固定止损' : isFloatingStopLossTriggered ? '浮动止损' : '最大余额',
          },
          '触发止损点或达到最大余额限制，停止买入',
        );
        return false;
      }

      return true;
    } catch (error) {
      logger.error({ error: error instanceof Error ? error.message : String(error) }, '检查钱包余额失败');
      return false;
    }
  }

  // 在类销毁时清理定时器
  public destroy() {
    if (this.walletBalanceInterval) {
      clearInterval(this.walletBalanceInterval);
      this.walletBalanceInterval = null;
    }

    // 清理所有代币轮询
    for (const [pollId, interval] of this.pollIntervals) {
      clearInterval(interval);
    }
    this.pollIntervals.clear();

    for (const [pollId, timeout] of this.pollTimeouts) {
      clearTimeout(timeout);
    }
    this.pollTimeouts.clear();

    this.soldTokens.clear();
  }

  async validate() {
    try {
      await getAccount(this.connection, this.config.quoteAta, this.connection.commitment);
    } catch (error) {
      logger.error(
        `在钱包中未找到 ${this.config.quoteToken.symbol} 代币账户: ${this.config.wallet.publicKey.toString()}`,
      );
      return false;
    }

    return true;
  }

  public async buy(accountId: PublicKey, poolState: LiquidityStateV4) {
    if (!this.todayAllowBuy) {
      logger.info('钱包余额不满足条件，跳过买入');
      return;
    }

    logger.trace({ mint: poolState.baseMint }, `处理新池子...`);

    // 在买入前就开始轮询检查,不等待它完成
    this.startPollingForToken(poolState.baseMint, accountId);

    if (this.config.oneTokenAtATime) {
      if (this.mutex.isLocked() || this.sellExecutionCount > 0) {
        logger.debug({ mint: poolState.baseMint.toString() }, `跳过买入，因为启用了单代币模式且已有代币正在处理中`);
        return;
      }

      await this.mutex.acquire();
    }

    try {
      // 异步方式
      const [market, mintAta] = await Promise.all([
        this.marketStorage.get(poolState.marketId.toString()),
        getAssociatedTokenAddressSync(poolState.baseMint, this.config.wallet.publicKey),
      ]);

      logger.info({ mintAta }, `mintAta`);

      const poolKeys: LiquidityPoolKeysV4 = createPoolKeys(accountId, poolState, market);

      logger.info({ poolKeys }, `poolKeys`);
      // 用异步检测result和shouldBuy
      const [result, shouldBuy] = await Promise.all([
        this.poolSizeFilter.execute(poolKeys),
        this.poolFilters.execute(poolKeys),
      ]);

      logger.info({ result }, `result`);
      logger.info({ shouldBuy }, `shouldBuy`);

      if (!result.ok || !shouldBuy) {
        logger.trace({ mint: poolKeys.baseMint.toString(), message: result.message }, `跳过买入，因为池子不匹配过滤器`);
        return;
      }

      // const shouldBuy = await this.poolFilters.execute(poolKeys);

      // logger.info({ shouldBuy }, `shouldBuy`);

      // if (!shouldBuy) {
      //   logger.trace({ mint: poolKeys.baseMint.toString() }, `跳过买入，因为池子不匹配过滤器`);
      //   return;
      // }

      for (let i = 0; i < this.config.maxBuyRetries; i++) {
        try {
          logger.info(
            { mint: poolState.baseMint.toString() },
            `Send buy transaction attempt: ${i + 1}/${this.config.maxBuyRetries}`,
          );
          const tokenOut = new Token(TOKEN_PROGRAM_ID, poolKeys.baseMint, poolKeys.baseDecimals);
          const result = await this.swap(
            poolKeys,
            this.config.quoteAta,
            mintAta,
            this.config.quoteToken,
            tokenOut,
            this.config.quoteAmount,
            this.config.buySlippage,
            this.config.wallet,
            'buy',
          );

          if (result.confirmed) {
            logger.info(
              {
                mint: poolState.baseMint.toString(),
                signature: result.signature,
                url: `https://solscan.io/tx/${result.signature}?cluster=${NETWORK}`,
              },
              `确认买入交易成功`,
            );
            return;
          }

          // 检查交易是否真的失败
          if (result.error?.includes('TransactionExpiredBlockheightExceededError') && result.signature) {
            // 检查交易是否已经成功
            const status = await this.connection1.getSignatureStatus(result.signature);
            if (
              status?.value?.confirmationStatus === 'confirmed' ||
              status?.value?.confirmationStatus === 'finalized'
            ) {
              logger.info(
                {
                  mint: poolState.baseMint.toString(),
                  signature: result.signature,
                },
                `交易已确认，但确认过程超时`,
              );
              return;
            }
          }

          logger.info(
            {
              mint: poolState.baseMint.toString(),
              signature: result.signature,
              error: result.error,
            },
            `确认买入交易失败`,
          );
        } catch (error) {
          logger.debug({ mint: poolState.baseMint.toString(), error }, `确认买入交易失败`);
        }
      }
    } catch (error) {
      logger.error({ mint: poolState.baseMint.toString(), error }, `买入交易失败`);
    } finally {
      if (this.config.oneTokenAtATime) {
        this.mutex.release();
      }
    }
  }

  public async sell(accountId: PublicKey, rawAccount: RawAccount) {
    if (this.config.oneTokenAtATime) {
      this.sellExecutionCount++;
    }

    try {
      logger.trace({ mint: rawAccount.mint }, `处理新代币...`);

      // 首先检查代币余额
      const tokenAccount = await getAccount(this.connection2, accountId);
      if (tokenAccount.amount === BigInt(0)) {
        logger.info({ mint: rawAccount.mint.toString() }, `代币余额为0，可能已经卖出成功，停止轮询检查`);
        this.stopPollingForToken(rawAccount.mint);
        return;
      }

      const poolData = await this.poolStorage.get(rawAccount.mint.toString());

      if (!poolData) {
        logger.trace({ mint: rawAccount.mint.toString() }, `代币池数据未找到，无法卖出`);
        return;
      }

      const tokenIn = new Token(TOKEN_PROGRAM_ID, poolData.state.baseMint, poolData.state.baseDecimal.toNumber());
      const tokenAmountIn = new TokenAmount(tokenIn, rawAccount.amount, true);

      if (tokenAmountIn.isZero()) {
        logger.info({ mint: rawAccount.mint.toString() }, `余额为空，无法卖出`);
        return;
      }

      if (this.config.autoSellDelay > 0) {
        logger.debug({ mint: rawAccount.mint }, `等待 ${this.config.autoSellDelay} ms 再卖出`);
        await sleep(this.config.autoSellDelay);
      }

      const market = await this.marketStorage.get(poolData.state.marketId.toString());
      const poolKeys: LiquidityPoolKeysV4 = createPoolKeys(new PublicKey(poolData.id), poolData.state, market);

      await this.priceMatch(tokenAmountIn, poolKeys);

      // 用于追踪已处理的交易
      const processedTransactions = new Set<string>();

      for (let i = 0; i < this.config.maxSellRetries; i++) {
        try {
          // 每次重试前检查代币是否已被轮询卖出
          if (this.soldTokens.has(rawAccount.mint.toString())) {
            logger.info({ mint: rawAccount.mint.toString() }, `代币已被轮询卖出，停止常规卖出重试`);
            return;
          }

          logger.info({ mint: rawAccount.mint }, `发送常规卖出交易尝试: ${i + 1}/${this.config.maxSellRetries}`);

          const result = await this.swap(
            poolKeys,
            accountId,
            this.config.quoteAta,
            tokenIn,
            this.config.quoteToken,
            tokenAmountIn,
            this.config.sellSlippage,
            this.config.wallet,
            'sell',
          );

          // 如果交易已经处理过，跳过
          if (result.signature && processedTransactions.has(result.signature)) {
            logger.info({ mint: rawAccount.mint.toString(), signature: result.signature }, `交易已处理过，跳过`);
            continue;
          }

          if (result.confirmed) {
            this.soldTokens.add(rawAccount.mint.toString());

            logger.info(
              {
                dex: `https://dexscreener.com/solana/${rawAccount.mint.toString()}?maker=${this.config.wallet.publicKey}`,
                mint: rawAccount.mint.toString(),
                signature: result.signature,
                url: `https://solscan.io/tx/${result.signature}?cluster=${NETWORK}`,
              },
              `常规卖出确认卖出交易成功,停止轮询`,
            );

            // 在卖出成功后检查余额并更新浮动止损点
            const tokenBalance = await this.connection1.getTokenAccountBalance(this.config.quoteAta);
            if (tokenBalance && tokenBalance.value) {
              const currentBalance = tokenBalance.value.uiAmount || 0;
              // 如果当前余额大于之前的浮动止损点,更新浮动止损点
              if (currentBalance > this.floatingStopLossPoint) {
                this.floatingStopLossPoint = currentBalance * 0.8;
                logger.info(
                  {
                    currentBalance,
                    newFloatingStopLoss: this.floatingStopLossPoint,
                  },
                  '更新浮动止损点',
                );
              }
            }

            this.stopPollingForToken(rawAccount.mint);
            return;
          }

          // 检查交易是否真的失败
          if (result.error?.includes('TransactionExpiredBlockheightExceededError') && result.signature) {
            // 检查交易是否已经成功
            const status = await this.connection2.getSignatureStatus(result.signature);
            if (
              status?.value?.confirmationStatus === 'confirmed' ||
              status?.value?.confirmationStatus === 'finalized'
            ) {
              logger.info(
                {
                  mint: rawAccount.mint.toString(),
                  signature: result.signature,
                },
                `卖出交易已确认，但确认过程超时`,
              );

              // 在卖出成功后检查余额并更新浮动止损点
              const tokenBalance = await this.connection0.getTokenAccountBalance(this.config.quoteAta);
              if (tokenBalance && tokenBalance.value) {
                const currentBalance = tokenBalance.value.uiAmount || 0;
                // 如果当前余额大于之前的浮动止损点,更新浮动止损点
                if (currentBalance > this.floatingStopLossPoint) {
                  this.floatingStopLossPoint = currentBalance * 0.8;
                  logger.info(
                    {
                      currentBalance,
                      newFloatingStopLoss: this.floatingStopLossPoint,
                    },
                    '更新浮动止损点',
                  );
                }
              }

              this.stopPollingForToken(rawAccount.mint);
              return;
            }
          }

          // 如果是余额不足错误，直接返回
          if (result.error?.includes('Insufficient funds') || result.error?.includes('Custom program error: 38')) {
            logger.info({ mint: rawAccount.mint.toString() }, `余额不足或已卖出，停止重试`);
            this.stopPollingForToken(rawAccount.mint);
            return;
          }

          // 记录已处理的交易
          if (result.signature) {
            processedTransactions.add(result.signature);
          }

          logger.info(
            {
              mint: rawAccount.mint.toString(),
              signature: result.signature,
              error: result.error,
            },
            `确认卖出交易失败`,
          );

          // 添加随机延迟，避免过快重试
          await sleep(Math.random() * 1000 + 500);
        } catch (error) {
          logger.debug({ mint: rawAccount.mint.toString(), error }, `确认卖出交易失败`);
        }
      }
    } catch (error) {
      logger.error({ mint: rawAccount.mint.toString(), error }, `Failed to sell token`);
    } finally {
      if (this.config.oneTokenAtATime) {
        this.sellExecutionCount--;
      }
    }
  }

  // noinspection JSUnusedLocalSymbols
  private async swap(
    poolKeys: LiquidityPoolKeysV4,
    ataIn: PublicKey,
    ataOut: PublicKey,
    tokenIn: Token,
    tokenOut: Token,
    amountIn: TokenAmount,
    slippage: number,
    wallet: Keypair,
    direction: 'buy' | 'sell',
  ) {
    const latestBlockhash = await this.connection1.getLatestBlockhash();
    const { innerTransaction } = Liquidity.makeSwapFixedInInstruction(
      {
        poolKeys: poolKeys,
        userKeys: {
          tokenAccountIn: ataIn,
          tokenAccountOut: ataOut,
          owner: wallet.publicKey,
        },
        amountIn: amountIn.raw,
        minAmountOut: 0,
      },
      poolKeys.version,
    );

    // 创建交易信息
    const messageV0 = new TransactionMessage({
      payerKey: wallet.publicKey,
      recentBlockhash: latestBlockhash.blockhash,
      instructions: [
        ...(this.isWarp || this.isJito
          ? []
          : [
              ComputeBudgetProgram.setComputeUnitPrice({ microLamports: this.config.unitPrice }),
              ComputeBudgetProgram.setComputeUnitLimit({ units: this.config.unitLimit }),
            ]),
        ...(direction === 'buy'
          ? [
              createAssociatedTokenAccountIdempotentInstruction(
                wallet.publicKey,
                ataOut,
                wallet.publicKey,
                tokenOut.mint,
              ),
            ]
          : []),
        ...innerTransaction.instructions,
        ...(direction === 'sell' ? [createCloseAccountInstruction(ataIn, wallet.publicKey, wallet.publicKey)] : []),
      ],
    }).compileToV0Message();

    // 创建交易签名
    const transaction = new VersionedTransaction(messageV0);
    transaction.sign([wallet, ...innerTransaction.signers]);

    return this.txExecutor.executeAndConfirm(transaction, wallet, latestBlockhash);
  }

  private async priceMatch(amountIn: TokenAmount, poolKeys: LiquidityPoolKeysV4) {
    let startTime = Date.now();

    //   150000; // 150秒
    const FORCE_SELL_TIMEOUT = this.poolSizeFilter.getTokenHoldTime();

    logger.info({ FORCE_SELL_TIMEOUT }, `常规卖出的持有时间`);

    if (this.config.priceCheckDuration === 0 || this.config.priceCheckInterval === 0) {
      return;
    }

    const timesToCheck = this.config.priceCheckDuration / this.config.priceCheckInterval;
    const profitFraction = this.config.quoteAmount.mul(this.config.takeProfit).numerator.div(new BN(100));
    const profitAmount = new TokenAmount(this.config.quoteToken, profitFraction, true);
    const takeProfit = this.config.quoteAmount.add(profitAmount);

    const lossFraction = this.config.quoteAmount.mul(this.config.stopLoss).numerator.div(new BN(100));
    const lossAmount = new TokenAmount(this.config.quoteToken, lossFraction, true);

    // 添加调试信息
    logger.debug(
      {
        timesToCheck,
        stopLossPercent: this.config.stopLoss,
        quoteAmount: this.config.quoteAmount.toFixed(),
        lossAmount: lossAmount.toFixed(),
        lossAmountRaw: lossAmount.raw.toString(),
        quoteAmountRaw: this.config.quoteAmount.raw.toString(),
      },
      '计算止损点前的调试信息',
    );

    // 添加安全检查，防止stopLoss计算结果为负数
    let stopLoss: TokenAmount;
    if (lossAmount.gt(this.config.quoteAmount)) {
      // 如果损失金额大于报价金额，设置stopLoss为0
      stopLoss = new TokenAmount(this.config.quoteToken, new BN(0), true);
      logger.warn(
        {
          stopLossPercent: this.config.stopLoss,
          quoteAmount: this.config.quoteAmount.toFixed(),
          lossAmount: lossAmount.toFixed(),
        },
        '止损百分比设置过高，将止损点设置为0',
      );
    } else {
      try {
        stopLoss = this.config.quoteAmount.subtract(lossAmount);
        logger.debug(
          {
            stopLoss: stopLoss.toFixed(),
            stopLossRaw: stopLoss.raw.toString(),
          },
          '成功计算止损点',
        );
      } catch (error) {
        logger.error(
          {
            error: error instanceof Error ? error.message : String(error),
            stopLossPercent: this.config.stopLoss,
            quoteAmount: this.config.quoteAmount.toFixed(),
            lossAmount: lossAmount.toFixed(),
          },
          '计算止损点时发生错误，将止损点设置为0',
        );
        stopLoss = new TokenAmount(this.config.quoteToken, new BN(0), true);
      }
    }

    const slippage = new Percent(this.config.sellSlippage, 100);
    let timesChecked = 0;

    do {
      try {
        const poolInfo = await Liquidity.fetchInfo({
          connection: this.connection,
          poolKeys,
        });

        const amountOut = Liquidity.computeAmountOut({
          poolKeys,
          poolInfo,
          amountIn: amountIn,
          currencyOut: this.config.quoteToken,
          slippage,
        }).amountOut;

        logger.debug(
          { mint: poolKeys.baseMint.toString() },
          `Take profit: ${takeProfit.toFixed()} | Stop loss: ${stopLoss.toFixed()} | Current: ${amountOut.toFixed()}`,
        );

        if (amountOut.lt(stopLoss) || amountOut.gt(takeProfit) || Date.now() - startTime >= FORCE_SELL_TIMEOUT) {
          // if (amountOut.lt(stopLoss) || Date.now() - startTime >= FORCE_SELL_TIMEOUT) {
          logger.info(
            { mint: poolKeys.baseMint.toString() },
            `触发${amountOut.lt(stopLoss) ? '止损' : '止盈'}点，执行卖出`,
          );
          break;
        }

        await sleep(this.config.priceCheckInterval);
      } catch (e) {
        logger.trace({ mint: poolKeys.baseMint.toString(), e }, `检查代币价格失败`);
      } finally {
        timesChecked++;
      }
    } while (timesChecked < timesToCheck);
  }

  private async startPollingForToken(mint: PublicKey, accountId: PublicKey) {
    const pollId = `${this.config.wallet.publicKey.toString()}_${mint.toString()}`;

    // 如果已经存在轮询，先清除
    if (this.pollIntervals.has(pollId)) {
      clearInterval(this.pollIntervals.get(pollId)!);
      this.pollIntervals.delete(pollId);
    }

    let lastKnownBalance: number | null = null;
    let startTime = Date.now();
    let consecutiveErrors = 0;
    const MAX_CONSECUTIVE_ERRORS = 5;

    logger.info({ tokenHoldTime: this.poolSizeFilter.getTokenHoldTime() }, `轮询开始等待时间`);

    // 延迟150秒后开始轮询
    // await sleep(150000);
    await sleep(this.poolSizeFilter.getTokenHoldTime());

    // 在开始轮询前先检查钱包中是否存在该代币账户
    try {
      const walletTokenAccount = await getAssociatedTokenAddress(mint, this.config.wallet.publicKey);
      const accountInfo = await this.connection0.getAccountInfo(walletTokenAccount);

      if (!accountInfo) {
        logger.info({ mint: mint.toString() }, `钱包中不存在该代币账户，说明买入未成功，停止轮询`);
        return;
      }

      // 验证账户所有者
      if (!accountInfo.owner.equals(TOKEN_PROGRAM_ID)) {
        logger.info({ mint: mint.toString() }, `代币账户所有者无效，说明买入未成功，停止轮询`);
        return;
      }

      // 解析代币账户数据
      const tokenAccount = AccountLayout.decode(accountInfo.data);
      const initialBalance = Number(tokenAccount.amount.toString());

      if (initialBalance === 0) {
        logger.info({ mint: mint.toString() }, `代币余额为0，说明买入未成功，停止轮询`);
        return;
      }

      lastKnownBalance = initialBalance;
      logger.info({ mint: mint.toString(), balance: initialBalance }, `记录初始代币余额`);
    } catch (error) {
      logger.info(
        { mint: mint.toString(), error: error instanceof Error ? error.message : String(error) },
        `检查钱包代币账户失败，说明买入未成功，停止轮询`,
      );
      return;
    }

    const checkTokenBalance = async () => {
      try {
        // 检查是否超过最大轮询时间
        if (Date.now() - startTime > this.MAX_POLL_DURATION) {
          logger.info({ mint: mint.toString() }, `轮询超时，停止检查代币余额`);
          this.stopPollingForToken(mint);
          return;
        }

        // 获取钱包中的代币账户信息
        const walletTokenAccount = await getAssociatedTokenAddress(mint, this.config.wallet.publicKey);
        const accountInfo = await this.connection0.getAccountInfo(walletTokenAccount);

        if (!accountInfo) {
          logger.info({ mint: mint.toString() }, `钱包中不存在该代币账户，可能已经卖出`);
          this.soldTokens.add(mint.toString());
          this.stopPollingForToken(mint);
          return;
        }

        // 验证账户所有者
        if (!accountInfo.owner.equals(TOKEN_PROGRAM_ID)) {
          logger.error({ mint: mint.toString() }, `代币账户所有者无效`);
          this.stopPollingForToken(mint);
          return;
        }

        // 解析代币账户数据
        const tokenAccount = AccountLayout.decode(accountInfo.data);
        const currentBalance = Number(tokenAccount.amount.toString());

        // 重置连续错误计数
        consecutiveErrors = 0;

        // 如果余额为0，说明已经卖出
        if (currentBalance === 0) {
          logger.info(
            { mint: mint.toString(), oldBalance: lastKnownBalance, newBalance: currentBalance },
            `检测到代币余额为0，可能已经卖出`,
          );
          this.stopPollingForToken(mint);
          return;
        }

        // 如果余额减少，说明部分卖出
        if (currentBalance < lastKnownBalance!) {
          logger.info(
            { mint: mint.toString(), oldBalance: lastKnownBalance, newBalance: currentBalance },
            `检测到代币余额减少，可能已经部分卖出`,
          );
          // 更新最后已知余额
          lastKnownBalance = currentBalance;
          return;
        }

        // 如果余额没有变化，执行卖出操作
        if (currentBalance === lastKnownBalance && currentBalance > 0) {
          logger.info({ mint: mint.toString(), balance: currentBalance }, `检测到代币余额未变化，执行轮询卖出操作`);

          // 构建RawAccount
          const rawAccount: RawAccount = {
            mint: mint,
            owner: tokenAccount.owner,
            amount: tokenAccount.amount,
            delegate: tokenAccount.delegate || mint,
            state: tokenAccount.state,
            isNative: tokenAccount.isNative,
            delegatedAmount: tokenAccount.delegatedAmount,
            closeAuthority: tokenAccount.closeAuthority || mint,
            delegateOption: tokenAccount.delegate ? 1 : 0,
            isNativeOption: tokenAccount.isNative ? 1 : 0,
            closeAuthorityOption: tokenAccount.closeAuthority ? 1 : 0,
          };

          // 执行轮询卖出操作
          const sellResult = await this.pollSell(mint, walletTokenAccount, rawAccount);

          if (sellResult) {
            this.stopPollingForToken(mint);
          }
          return;
        }

        // 更新最后已知余额
        lastKnownBalance = currentBalance;
      } catch (error) {
        consecutiveErrors++;
        logger.error(
          {
            mint: mint.toString(),
            error: error instanceof Error ? error.message : String(error),
            consecutiveErrors,
          },
          `检查代币余额失败`,
        );

        // 如果连续错误次数过多，停止轮询
        if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
          logger.error({ mint: mint.toString() }, `连续错误次数过多，停止轮询`);
          this.stopPollingForToken(mint);
          return;
        }
      }
    };

    // 开始轮询
    const interval = setInterval(checkTokenBalance, this.POLL_INTERVAL);
    this.pollIntervals.set(pollId, interval);

    // 设置最大轮询时间
    const timeout = setTimeout(() => {
      this.stopPollingForToken(mint);
    }, this.MAX_POLL_DURATION);
    this.pollTimeouts.set(pollId, timeout);
  }

  private stopPollingForToken(mint: PublicKey) {
    const pollId = `${this.config.wallet.publicKey.toString()}_${mint.toString()}`;

    if (this.pollIntervals.has(pollId)) {
      clearInterval(this.pollIntervals.get(pollId)!);
      this.pollIntervals.delete(pollId);
    }

    if (this.pollTimeouts.has(pollId)) {
      clearTimeout(this.pollTimeouts.get(pollId)!);
      this.pollTimeouts.delete(pollId);
    }
  }

  private async pollSell(mint: PublicKey, accountId: PublicKey, rawAccount: RawAccount) {
    try {
      logger.info({ mint: mint.toString() }, `执行轮询卖出操作`);

      const poolData = await this.poolStorage.get(mint.toString());
      if (!poolData) {
        logger.warn({ mint: mint.toString() }, `代币池数据未找到，无法卖出`);
        return false;
      }

      const tokenIn = new Token(TOKEN_PROGRAM_ID, poolData.state.baseMint, poolData.state.baseDecimal.toNumber());
      const tokenAmountIn = new TokenAmount(tokenIn, rawAccount.amount, true);

      if (tokenAmountIn.isZero()) {
        logger.warn({ mint: mint.toString() }, `余额为空，无法卖出`);
        return false;
      }

      const market = await this.marketStorage.get(poolData.state.marketId.toString());
      const poolKeys: LiquidityPoolKeysV4 = createPoolKeys(new PublicKey(poolData.id), poolData.state, market);

      const result = await this.swap(
        poolKeys,
        accountId,
        this.config.quoteAta,
        tokenIn,
        this.config.quoteToken,
        tokenAmountIn,
        this.config.sellSlippage,
        this.config.wallet,
        'sell',
      );

      if (result.confirmed) {
        // 标记代币已卖出
        this.soldTokens.add(mint.toString());
        logger.info(
          {
            mint: mint.toString(),
            signature: result.signature,
            url: `https://solscan.io/tx/${result.signature}?cluster=${NETWORK}`,
          },
          `轮询卖出成功`,
        );
        return true;
      }

      logger.warn(
        {
          mint: mint.toString(),
          signature: result.signature,
          error: result.error,
        },
        `轮询卖出失败`,
      );
      return false;
    } catch (error) {
      logger.error(
        { mint: mint.toString(), error: error instanceof Error ? error.message : String(error) },
        `轮询卖出操作失败`,
      );
      return false;
    }
  }
}
